package icasue.reflect.handles;

import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * @Author: Qiao Hang
 * @CreateDate: 2020/11/30 上午11:44
 * @UpdateDate:
 * @Description:
 */
public class HandleSupplier {

    private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
            | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
    public static final MethodHandles.Lookup methodLookup;
    public static final Class currentLookUpClass;

    static {
        //get lookup
        methodLookup = reFindLookUp(MethodHandles.Lookup.class);
        currentLookUpClass = MethodHandles.Lookup.class;
    }

    private static MethodHandles.Lookup reFindLookUp(Class cls){
        //get lookup
        MethodHandles.Lookup lookupTemp = null;
        try {
            //java 9
            Method lookupMethod = MethodHandles.class.getDeclaredMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
            lookupMethod.setAccessible(true);
            MethodHandles.Lookup lookup =
                    (MethodHandles.Lookup)lookupMethod.invoke(null, cls, MethodHandles.lookup());
            lookupTemp = lookup;
        }catch (Exception e){
            //java 8
            try {
                Constructor<MethodHandles.Lookup> lookupConstruct = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
                lookupConstruct.setAccessible(true);
                lookupTemp = lookupConstruct.newInstance(cls, ALLOWED_MODES);
            } catch (Exception e1) {
                throw new RuntimeException(e1.getCause());
            }
        }
        return lookupTemp;
    }


    /**
     * Supply four FunctionInterface plus.
     * @param <R>
     */
    @FunctionalInterface
    public interface FunctionAry<R> extends Function<Object[],R> {
        @Override
        R apply(Object... objects);
    }

    @FunctionalInterface
    public interface PredicateAry extends Predicate<Object[]> {
        @Override
        boolean test(Object... objects);
    }

    @FunctionalInterface
    public interface ConsumerAry extends Consumer<Object[]> {
        @Override
        void accept(Object... objects);
    }

    @FunctionalInterface
    public interface SupplierAry extends Supplier<Object[]> {
        @Override
        Object[] get();
    }

}
